﻿#include <xy/ais/surface.h>
#include <xy/alg/projection.h>

namespace xy
{

namespace ais
{

using namespace xy::alg;
using namespace xy::geom;

surface::surface(std::shared_ptr<geom::surface<3>> surface) : mesh(nullptr), m_surface(surface)
{
}

void surface::sample(std::size_t num)
{
    m_meshData = std::make_shared<geom::mesh>();
    auto [u1, u2] = m_surface->bound(0);
    auto [v1, v2] = m_surface->bound(1);
    std::size_t sample = num * num;
    
    m_meshData->vertices.reserve(sample);
    m_meshData->normals.reserve(sample);
    m_meshData->texcoords.reserve(sample);
    for (std::size_t i = 0; i < num; ++i)
    {
        double u = u1 + (u2 - u1) * i / (num - 1);
        for (std::size_t j = 0; j < num; ++j)
        {
            double v = v1 + (v2 - v1) * j / (num - 1);

            auto [P, DU, DV] = m_surface->d1({u, v});
            m_meshData->vertices.emplace_back(convert<float>(P));
            m_meshData->normals.emplace_back(convert<float>(cross(DU, DV)));
            m_meshData->texcoords.emplace_back(float(u), float(v));
        }
    }

    m_meshData->indices.reserve((num - 1) * (num - 1) * 2);
    for (std::size_t i = 0; i < num - 1; ++i)
    {
        for (std::size_t j = 0; j < num - 1; ++j)
        {
            std::size_t idx = i * num + j;
            m_meshData->indices.emplace_back(idx);
            m_meshData->indices.emplace_back(idx + num);
            m_meshData->indices.emplace_back(idx + 1);

            m_meshData->indices.emplace_back(idx + num);
            m_meshData->indices.emplace_back(idx + num + 1);
            m_meshData->indices.emplace_back(idx + 1);
        }
    }
}

std::shared_ptr<geom::surface<3>> surface::sur() const
{
    return m_surface;
}

bool surface::collision(msg_mouse *msg)
{
    // 获得屏幕点和鼠标位置
    vec3f P = msg->spaceMapping(msg->movePos);
    vec3f emit = (P - msg->camera->position()).normalize();

    vec3 Pd = convert<double>(P);
    vec3 emitd = convert<double>(emit);

    auto [U1, U2] = m_surface->bound(0);
    auto [V1, V2] = m_surface->bound(1);
    auto uv = projection_point<3>::direction(m_surface, Pd, emitd, (U1 + U2) / 2, (V1 + V2) / 2, 1e-2);

    // 如果小于指定深度，则记录下来
    auto Q = m_surface->d0(uv);
    auto S = msg->screenMapping(convert<float>(Q));
    if (S.z() < msg->depth && cross((Pd - Q).normalize(), emitd).modulus() < 1e-2)
    {
        msg->depth = S.z();
        msg->captured = this;
        return true;
    }
    return false;
}

void surface::visit(msg_init *msg)
{
    // 利用工厂创建渲染器
    if (msg->factory)
        m_renderer.reset(msg->factory->create_product("surface"));
}

} // namespace ais

} // namespace xy
